package dryven.unittest;

import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;

import dryven.model.binding.fieldset.BoundField;
import dryven.model.binding.fieldset.Field;
import dryven.model.binding.fieldset.FieldSet;
import dryven.model.binding.form.Form;
import dryven.model.binding.proxy.Binding;
import dryven.model.binding.proxy.BindingProxy;
import dryven.model.binding.proxy.CallInterceptorStubClassLoader;
import dryven.unittest.models.ExampleModel;
import junit.framework.TestCase;

public class BindingTestCase extends TestCase {
	
	public void testProperty() {
		BindingProxy proxy = new BindingProxy();
		ExampleModel model = proxy.byClass(ExampleModel.class);

		assertFalse(model.getClass().equals(ExampleModel.class));
		assertTrue(model.getClass().getName().equals(new CallInterceptorStubClassLoader(getClass().getClassLoader()).getProxyClassName(ExampleModel.class)));
		
		BoundField bf = proxy.newField(model.getAge());
		assertTrue(bf.getGetter().getName().equals("getAge"));
		assertTrue(bf.getGetter().getDeclaringClass().equals(ExampleModel.class));
		assertTrue(bf.getSetter().getName().equals("setAge"));
		assertTrue(bf.getSetter().getDeclaringClass().equals(ExampleModel.class));
		
		boolean failed = false;
		try {
			model.setAge(10);
		} catch(UnsupportedOperationException e) {
			failed = true;
		}
		assertTrue(failed);
		
		bf = proxy.newField(model.getSomeDependendValue("hello", "world"));
		assertTrue(bf.getGetter().getName().equals("getSomeDependendValue"));
		assertTrue(bf.getGetter().getDeclaringClass().equals(ExampleModel.class));
		assertTrue(bf.getSetter()==null);		
		
		bf = proxy.newField(model.getBirthday());
		assertTrue(bf.getGetter().getName().equals("getBirthday"));
		assertTrue(bf.getGetter().getDeclaringClass().equals(ExampleModel.class));
		assertTrue(bf.getSetter().getName().equals("setBirthday"));
		assertTrue(bf.getSetter().getDeclaringClass().equals(ExampleModel.class));
		
		bf = proxy.newField(model.getLastname());
		assertTrue(bf.getGetter().getName().equals("getLastname"));
		assertTrue(bf.getGetter().getDeclaringClass().equals(ExampleModel.class));
		assertTrue(bf.getSetter().getName().equals("setLastname"));
		assertTrue(bf.getSetter().getDeclaringClass().equals(ExampleModel.class));
		
		bf = proxy.newField(model.getExperience());
		assertTrue(bf.getGetter().getName().equals("getExperience"));
		assertTrue(bf.getGetter().getDeclaringClass().equals(ExampleModel.class));
		assertTrue(bf.getSetter().getName().equals("setExperience"));
		assertTrue(bf.getSetter().getDeclaringClass().equals(ExampleModel.class));
		
		bf = proxy.newField(model.getFilesize());
		assertTrue(bf.getGetter().getName().equals("getFilesize"));
		assertTrue(bf.getGetter().getDeclaringClass().equals(ExampleModel.class));
		assertTrue(bf.getSetter().getName().equals("setFilesize"));
		assertTrue(bf.getSetter().getDeclaringClass().equals(ExampleModel.class));
		
		bf = proxy.newField(model.getSavings());
		assertTrue(bf.getGetter().getName().equals("getSavings"));
		assertTrue(bf.getGetter().getDeclaringClass().equals(ExampleModel.class));
		assertTrue(bf.getSetter().getName().equals("setSavings"));
		assertTrue(bf.getSetter().getDeclaringClass().equals(ExampleModel.class));
		
		bf = proxy.newField(model.isMale());
		assertTrue(bf.getGetter().getName().equals("isMale"));
		assertTrue(bf.getGetter().getDeclaringClass().equals(ExampleModel.class));
		assertTrue(bf.getSetter().getName().equals("setMale"));
		assertTrue(bf.getSetter().getDeclaringClass().equals(ExampleModel.class));
		
		bf = proxy.newField(model.getTcpPort());
		assertTrue(bf.getGetter().getName().equals("getTcpPort"));
		assertTrue(bf.getGetter().getDeclaringClass().equals(ExampleModel.class));
		assertTrue(bf.getSetter().getName().equals("setTcpPort"));
		assertTrue(bf.getSetter().getDeclaringClass().equals(ExampleModel.class));
		
		bf = proxy.newField(model.getFlags());
		assertTrue(bf.getGetter().getName().equals("getFlags"));
		assertTrue(bf.getGetter().getDeclaringClass().equals(ExampleModel.class));
		assertTrue(bf.getSetter().getName().equals("setFlags"));
		assertTrue(bf.getSetter().getDeclaringClass().equals(ExampleModel.class));
		assertTrue(bf.getName().equals("flags"));
	}
	
	public void testField() throws SecurityException, NoSuchMethodException {
		BindingProxy proxy = new BindingProxy();
		ExampleModel model = proxy.byClass(ExampleModel.class);
		
		Binding<String> binding1 = proxy.newBinding(model.getLastname());
		Binding<Calendar> binding2 = proxy.newBinding(model.getBirthday());
		Binding<Integer> binding3 = proxy.newBinding(model.getAge());
		
		assertTrue(binding1.getBoundMethod().equals(ExampleModel.class.getMethod("getLastname", new Class<?>[]{})));
		assertTrue(binding2.getBoundMethod().equals(ExampleModel.class.getMethod("getBirthday", new Class<?>[]{})));
		assertTrue(binding3.getBoundMethod().equals(ExampleModel.class.getMethod("getAge", new Class<?>[]{})));
		
		String name = "Duke";
		Field f = proxy.newField(model.getName());
		ExampleModel setterModel = new ExampleModel();
		f.setValue(setterModel, name);
		
		assertTrue(f.getValue(setterModel).equals(name));
		assertTrue(setterModel.getName().equals(name));
	}
	
	public FieldSet createFields() {
		BindingProxy proxy = new BindingProxy();
		ExampleModel model = proxy.byClass(ExampleModel.class);
		
		return new FieldSet().getDefaultGroup().add(
				proxy.newField(model.getName()),
				proxy.newField(model.getLastname()),
				proxy.newField(model.getBirthday()),
				proxy.newField(model.getExperience())
			).finishGroup();
	}
	
	public void testEncode() throws SecurityException, NoSuchMethodException {
		Method m = this.getClass().getMethod("editFields", new Class<?>[]{Integer.TYPE,String.class});
		Binding<FieldSet> b = new Binding<FieldSet>(m);
		assertEquals(this.getClass().getName()+":editFields:int,java.lang.String", b.toString());
		Binding<FieldSet> c = new Binding<FieldSet>(b.toString());
		assertTrue(b.getBoundMethod().equals(c.getBoundMethod()));
	}
	
	public FieldSet editFields(int i, String b) {
		return null;
	}
	
	public void testInvoke() {
		BindingProxy proxy = new BindingProxy();
		BindingTestCase c = proxy.byObject(this);
		Binding<Integer> b = proxy.newBinding(c.square(5));
		assertTrue(b.invoke().equals(25));
		Binding<String> s = proxy.newBinding(c.greet("world"));
		assertTrue(s.invoke().equals("hello world"));
	}
	
	public int square(int a) {
		return a*a;
	}
	
	public String greet(String name) {
		return "hello "+name;
	}
	
	public void testParams() {
		BindingProxy proxy = new BindingProxy();
		ExampleModel modelStub = proxy.byClass(ExampleModel.class);
		//still can bind with primitives though
		Binding<String> b = proxy.newBinding(modelStub.getSomeDependendValue("hello", "world"));
		assertTrue(b.getBoundParams()[0].equals("hello"));
		assertTrue(b.getBoundParams()[1].equals("world"));
	}
}
